
#include <stdio.h>
#include <time.h>
#include <stdlib.h>

#define fp(x) fputs(x, fo)
char para_num_buf[8];
const char* para_num(int i)
{
    sprintf(para_num_buf, "%d", i);
    return para_num_buf;
}

void write_temp_para(FILE*& fo, int pc, bool has_rtn)
{
    if(has_rtn || pc > 0) fp("(Of ");
    for(int i = 0; i < pc; i++)
    {
        if(i > 0) fp(", ");
        fp("T");
        fp(para_num(i));
    }
    if(has_rtn)
    {
        if(pc > 0) fp(", ");
        fp("RT");
    }
    if(has_rtn || pc > 0) fp(")");
}

void write_head(FILE*& fo, int pc, const char* fn, const char* dn, bool has_rtn, bool para_byref, bool has_false_value)
{
    fp("    Public ");
    if(has_rtn) fp("Function");
    else fp("Sub");
    fp(" ");
    fp(fn);
    write_temp_para(fo, pc, has_rtn);
    fp(" _"
       "\n                    (ByVal d As ");
    fp(dn);
    write_temp_para(fo, pc, has_rtn);
    for(int i = 0; i < pc; i++)
    {
        fp(",\n                     ");
        if(para_byref) fp("ByRef");
        else fp("ByVal");
        fp(" i");
        fp(para_num(i));
        fp(" As T");
        fp(para_num(i));
    }
    if(has_rtn && has_false_value) fp(",\n                     ByVal false_value As RT");
    fp(")");
    if(has_rtn) fp(" As RT");
    fp("\n");
}

void write_body(FILE*& fo, int pc, bool has_rtn, bool no_throw)
{
    if(no_throw)
    {
        fp("        assert(Not d Is Nothing)");
    }
    else
    {
        fp("        If d Is Nothing Then\n            Return");
        if(has_rtn) fp(" false_value");
        fp("\n        End If");
    }
    fp("\n        Try"
       "\n            ");
    if(has_rtn) fp("Return ");
    fp("d(");
    for(int i = 0; i < pc; i++)
    {
        if(i > 0) fp(", ");
        fp("i");
        fp(para_num(i));
    }
    fp(")");
    if(no_throw)
    {
        fp("\n        Catch ex As Exception"
           "\n            log_unhandled_exception(ex)"
           "\n            assert(False)"
           "\n            Return");
        if(has_rtn) fp(" Nothing");
    }
    else
    {
        fp("\n        Catch ex As ThreadAbortException"
           "\n            raise_error(error_type.warning, \"thread abort\")"
           "\n            Return");
        if(has_rtn) fp(" false_value");
        fp("\n        Catch ex As Exception"
           "\n            log_unhandled_exception(ex)"
           "\n            Return");
        if(has_rtn) fp(" false_value");
    }
    fp("\n        End Try");
    if(has_rtn)
    fp("\n    End Function\n\n");
    else
    fp("\n    End Sub\n\n");
}

void write(FILE*& fo, int pc, const char* fn, const char* dn, bool has_rtn, bool para_byref)
{
    write_head(fo, pc, fn, dn, has_rtn, para_byref, true);
    write_body(fo, pc, has_rtn, false);
}

void write2(FILE*& fo, int pc, const char* fn, const char* dn, bool has_rtn, bool para_byref)
{
    write_head(fo, pc, fn, dn, has_rtn, para_byref, false);
    write_body(fo, pc, has_rtn, true);
}


#undef fp

void write(FILE*& fo, int pc)
{
    if(pc > 0)
    {
        write(fo, pc, "do_", "_do", true, true);
        write(fo, pc, "void_", "void", false, true);
        write2(fo, pc, "nothrow", "_do", true, true);
        write2(fo, pc, "nothrow", "void", false, true);
    }
    // .net 3.5 has only Action / Func with 4 parameters
    if(pc <= 4)
    {
        write(fo, pc, "do_", "Func", true, false);
        write(fo, pc, "void_", "Action", false, false);
        write2(fo, pc, "nothrow", "Func", true, true);
        write2(fo, pc, "nothrow", "Action", false, true);
    }
}

int main(int argc, char* argv[])
{
    if(argc < 2)
        printf("need a parameter for output file.\n");
    else
    {
        const int para_count = 5;
        FILE* fo = fopen(argv[1], "w");

        fputs("\n'this file is generated by osi/root/codegen/delegate/delegate.exe"
              "\n'so edit osi/root/codegen/delegate/delegate.cpp instead of this file\n"
              "\nOption Explicit On"
              "\nOption Infer Off"
              "\nOption Strict On\n"
              "\nImports System.Threading"
              "\nImports osi.root.constants"
              "\nImports osi.root.delegates\n"
              "\nPublic Module _delegate\n", fo);

        for(int i = 0; i <= para_count; i++)
            write(fo, i);

        fputs("End Module\n", fo);

        fclose(fo);
    }

    return 0;
}

